﻿#region Copyright

// The contents of this file are subject to the Mozilla Public License
//  Version 1.1 (the "License"); you may not use this file except in compliance
//  with the License. You may obtain a copy of the License at
//  
//  http://www.mozilla.org/MPL/
//  
//  Software distributed under the License is distributed on an "AS IS"
//  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
//  License for the specific language governing rights and limitations under 
//  the License.
//  
//  The Initial Developer of the Original Code is Robert Smyth.
//  Portions created by Robert Smyth are Copyright (C) 2008.
//  
//  All Rights Reserved.

#endregion

using System;
using System.Collections.Generic;
using System.Diagnostics;
using NUnitGrid.Framework;
using NUnitGrid.Framework.Logging;
using NUnitGrid.Framework.Pipes;
using NUnitGrid.Protocol;
using NUnitGrid.Protocol.Commands;
using NUnitGrid.Server.Plugins.Nodes.Node;
using NUnitGrid.Server.Plugins.Nodes.Node.States;


namespace NUnitGrid.Server.Plugins.Clients.Client
{
    public class ClientProxy : IClientProxy, IReadInfoListener, IPipeListener, IRunOnGridTaskListener
    {
        private readonly IGridLoaderService gridLoaderService;
        private readonly IGridNodeMetrics gridNodeMetrics;
        private readonly IGridNodeListener<IGridNodeMetrics> listener;
        private readonly ILogger logger;

        private readonly Dictionary<IRunOnGridTask, ClientRunOnGridTask> pendingTasks =
            new Dictionary<IRunOnGridTask, ClientRunOnGridTask>();

        private readonly IGridProtocol protocol;

        private readonly Dictionary<IRunOnGridTask, ClientRunOnGridTask> runningTasks =
            new Dictionary<IRunOnGridTask, ClientRunOnGridTask>();

        private readonly IStateEngine<IGridNodeState> stateEngine;

        private readonly Stopwatch taskStopWatch = new Stopwatch();

        public ClientProxy(IStateEngine<IGridNodeState> stateEngine, IServerStateFactory serverStateFactory,
                           ILogger logger, IGridNodeListener<IGridNodeMetrics> listener, IGridProtocol protocol,
                           IGridLoaderService gridLoaderService,
                           IGridNodeMetrics gridNodeMetrics)
        {
            this.stateEngine = stateEngine;
            this.logger = logger;
            this.listener = listener;
            this.protocol = protocol;
            this.gridLoaderService = gridLoaderService;
            this.gridNodeMetrics = gridNodeMetrics;
            stateEngine.ChangeState(serverStateFactory.CreateLinkNotConnectedState());
        }

        public string MachineName
        {
            get { return gridNodeMetrics.MachineName; }
        }

        public int NumberOfProcessors
        {
            get { return gridNodeMetrics.NumberOfProcessors; }
        }

        public int ProcessorMHz
        {
            get { return gridNodeMetrics.ProcessorMHz; }
        }

        public string SoftwareVersion
        {
            get { return gridNodeMetrics.SoftwareVersion; }
        }

        public bool Busy
        {
            get { return RunningTasks != 0; }
        }

        public int RunningTasks
        {
            get { return runningTasks.Count; }
        }

        public int PendingTasks
        {
            get { return pendingTasks.Count; }
        }

        public int TasksCount { get; private set; }

        public TimeSpan CurrentTaskRunTime
        {
            get { return taskStopWatch.Elapsed; }
        }

        public int FailedTasksCount { get; private set; }

        public string Nickname
        {
            get { return MachineName; }
        }

        public void StopAllGridTasks()
        {
            gridLoaderService.StopAllGridTasks(this);
        }

        public void Start()
        {
            taskStopWatch.Reset();
            protocol.Start();
            stateEngine.CurrentState.Start();
        }

        public void RunTask(IRunOnGridTask task, IRunOnGridTaskListener taskProgressListener)
        {
            TasksCount++;
            pendingTasks.Add(task, new ClientRunOnGridTask(taskProgressListener));
            gridLoaderService.RunTask(this, task, this);
        }

        public void Stop()
        {
            pendingTasks.Clear();
            runningTasks.Clear();
            protocol.Stop();
        }

        public void OnMetricsUpdate(IGridMetrics gridMetrics)
        {
            protocol.SendMetricsUpdate(gridMetrics.BusyNodesCount, gridMetrics.OnlineNodesCount,
                                       gridMetrics.PendingTasksCount);
        }

        void IPipeListener.OnConnectedChanged(bool connected)
        {
            logger.Verbose.Write("Node's link connected - {0}", connected);
            stateEngine.CurrentState.OnConnectedChanged(connected);
        }

        void IReadInfoListener.OnInfoRead(bool succeeded, string nodeMachineName, string nickName,
                                          int nodeNumberOfProcessors, int nodeProcessorMHz, string softwareVersion)
        {
            if (succeeded)
            {
                logger.Verbose.Write("Client information read");

                if (gridNodeMetrics.Update(nodeMachineName, nickName, nodeNumberOfProcessors, nodeProcessorMHz,
                                           softwareVersion))
                {
                    OnChange();
                }
            }
        }

        void IRunOnGridTaskListener.OnTaskStarted(IRunOnGridTask sender)
        {
            taskStopWatch.Reset();
            taskStopWatch.Start();
            var task = sender;
            var pendingTask = pendingTasks[task];
            pendingTasks.Remove(task);
            runningTasks.Add(task, pendingTask);
            OnChange();

            pendingTask.OnTaskStarted(sender);
        }

        void IRunOnGridTaskListener.OnTaskCompleted(IRunOnGridTask sender, bool succeeded, string textOutput,
                                                    string machineName)
        {
            taskStopWatch.Stop();
            if (!succeeded)
            {
                FailedTasksCount++;
            }

            ClientRunOnGridTask nodeRunOnGridTask;
            var task = sender;
            if (runningTasks.ContainsKey(task))
            {
                nodeRunOnGridTask = runningTasks[task];
                runningTasks.Remove(task);
            }
            else
            {
                nodeRunOnGridTask = pendingTasks[task];
                pendingTasks.Remove(task);
            }

            OnChange();

            nodeRunOnGridTask.OnTaskCompleted(sender, succeeded, textOutput, machineName);
        }

        private void OnChange()
        {
            protocol.SendClientGridLoadingUpdate(PendingTasks, RunningTasks);
            listener.OnChange(gridNodeMetrics);
        }
    }
}